#include "glProgram.h"

#include <stdio.h>
#include <string.h>

#include "glError.h"

static char g_FileName[] = "glProgram.cpp";

#pragma warning(disable: 4996) // disable function || variable may be unsafe warning

CglProgram::CglProgram(void){}

CglProgram::~CglProgram(void) { Delete(); }

bool CglProgram::Create(GLhandleARB vs, GLhandleARB ps)
{
    m_ID = glCreateProgramObjectARB();

    glAttachShader(m_ID, vs);
    glAttachShader(m_ID, ps);

    glLinkProgram(m_ID);

    int32 result = 0;
    glGetObjectParameterivARB(m_ID, GL_OBJECT_LINK_STATUS_ARB, &result);

    if (result)
        sprintf(m_Log, "GLSL program created successfully");
    else
        glGetInfoLogARB(m_ID, 1024, &m_LogLength, m_Log);

    return (result ? true : false);
}

bool CglProgram::Create(GLhandleARB vs, GLhandleARB gs, GLhandleARB ps)
{
    m_ID = glCreateProgram();

    glAttachShader(m_ID, vs);
    glAttachShader(m_ID, ps);
    glAttachShader(m_ID, gs);

    glLinkProgram(m_ID);

    int32 result = 0;
    glGetObjectParameterivARB(m_ID, GL_OBJECT_LINK_STATUS_ARB, &result);

    if (result)
        sprintf(m_Log, "GLSL program created successfully");
    else
        glGetInfoLogARB(m_ID, 1024, &m_LogLength, m_Log);

    return (result ? true : false);
}

void CglProgram::Delete()
{
    glDeleteProgram(m_ID);
    m_ID = 0;
}

void CglProgram::Enable(void)
{
    glUseProgram(m_ID);
}

void CglProgram::Disable(void)
{
    glUseProgram(0);
}

void CglProgram::setUniform1f(const char *name, float value)
{
    GLint loc = glGetUniformLocation(m_ID, name);

    if (loc >= 0) glUniform1f(loc, value);
#if _DEBUG 
    else fprintf(stderr, "Error setting parameter '%s'\n", name);
#endif
}

void CglProgram::setUniform2f(const char *name, float x, float y)
{
    GLint loc = glGetUniformLocation(m_ID, name);
    
    if (loc >= 0) glUniform2f(loc, x, y); 
#if _DEBUG
    else fprintf(stderr, "Error setting parameter '%s'\n", name);
#endif
}

void CglProgram::setUniform3f(const char *name, float x, float y, float z)
{
    GLint loc = glGetUniformLocation(m_ID, name);

    if (loc >= 0) glUniform3f(loc, x, y, z);
#if _DEBUG
    else fprintf(stderr, "Error setting parameter '%s'\n", name);
#endif
}

void CglProgram::setUniform4f(const char *name, float x, float y, float z, float w)
{
    GLint loc = glGetUniformLocation(m_ID, name);
   
    if (loc >= 0) glUniform4f(loc, x, y, z, w);
#if _DEBUG
    else fprintf(stderr, "Error setting parameter '%s'\n", name);
#endif
}

void CglProgram::setUniformMatrix4fv(const GLchar *name, GLfloat *m, bool transpose)
{
    GLint loc = glGetUniformLocation(m_ID, name);
    
    if (loc >= 0) glUniformMatrix4fv(loc, 1, transpose, m);
#if _DEBUG
    else fprintf(stderr, "Error setting parameter '%s'\n", name);
#endif
}

void CglProgram::setUniformfv(const GLchar *name, GLfloat *v, int elementSize, int count)
{
    GLint loc = glGetUniformLocation(m_ID, name);
    if (loc == -1) 
    {
#ifdef _DEBUG
        fprintf(stderr, "Error setting parameter '%s'\n", name);
#endif
        return;
    }

    switch (elementSize) 
    {
        case 1:
            glUniform1fv(loc, count, v);
            break;
        case 2:
            glUniform2fv(loc, count, v);
            break;
        case 3:
            glUniform3fv(loc, count, v);
            break;
        case 4:
            glUniform4fv(loc, count, v);
            break;
        default:
            fprintf(stderr, "Invalid elementSize value '%d'\n", elementSize);
            break;
    }
}

void CglProgram::bindTexture(const char *name, GLuint tex, GLenum target, GLint unit)
{
    GLint loc = glGetUniformLocation(m_ID, name);
    if (loc >= 0) 
    { 
        glUseProgram(m_ID);
        glActiveTexture(GL_TEXTURE0 + unit);
        glBindTexture(target, tex);
        glUniform1i(loc, unit);
        glActiveTexture(GL_TEXTURE0);
    } 
#if _DEBUG
    else fprintf(stderr, "Error binding texture '%s'\n", name);
#endif
}

